/*
 * Copyright (c) 2024. hallele, Inc All rights reserved.
 */

package cn.hallele.ms.infrastructure.core.aspect.controller.children;

import cn.hallele.infrastructure.metadata.constant.MsgCodeConst;
import cn.hallele.infrastructure.metadata.constant.SymbolConstant;
import cn.hallele.infrastructure.metadata.exception.HalException;
import cn.hallele.infrastructure.metadata.exception.children.ActionException;
import cn.hallele.infrastructure.metadata.exception.children.ParamException;
import cn.hallele.infrastructure.metadata.exception.children.SecurityException;
import cn.hallele.infrastructure.metadata.exception.children.ServiceCallException;
import cn.hallele.infrastructure.metadata.io.Response;
import cn.hallele.infrastructure.util.ObjectUtil;
import cn.hallele.ms.infrastructure.core.aspect.controller.BaseExceptionAspect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.annotation.Order;
import org.springframework.validation.BindException;
import org.springframework.validation.method.ParameterValidationResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.HandlerMethodValidationException;

import java.util.List;
import java.util.StringJoiner;

/**
 * hallele_mc_core
 *
 * @author anle5
 * @since 2024/9/15 0:33
 */
@Slf4j
@RestControllerAdvice
@Order(10)
public class ComponentExceptionAspect extends BaseExceptionAspect {
    /**
     * 拦截所有valid组件的 BindException 的报错，
     *
     * @param e {@link BindException}
     * @return 错误响应
     */
    @ExceptionHandler(BindException.class)
    public Response<?> bindException(BindException e) {
        StringJoiner joiner = new StringJoiner(SymbolConstant.COMMA);
        e.getFieldErrors().forEach(fieldError -> {
            joiner.add(fieldError.getField());
        });
        String msgDesc = i18n.getMessage(
                MsgCodeConst.PARAMETER_VALIDATE_FAILED,
                new Object[]{joiner.toString(), ((MethodArgumentNotValidException) e).getBody().getDetail()},
                MsgCodeConst.FAILED,
                getLocale());
        Object[] detailMessageArguments = ((MethodArgumentNotValidException) e).getDetailMessageArguments();
        Object message = SymbolConstant.UN_KNOWN;
        if (detailMessageArguments != null && detailMessageArguments.length == 2) {
            message = detailMessageArguments[1];
        }
        log.error("Find a BindException. MsgCode: {}, msgDesc: {}, details: {}",
                MsgCodeConst.PARAMETER_VALIDATE_FAILED, msgDesc, message, e);
        return buildResponse(MsgCodeConst.PARAMETER_VALIDATE_FAILED, msgDesc);
    }

    /**
     * 拦截所有valid组件的 HandlerMethodValidationException 的报错
     *
     * @param e {@link HandlerMethodValidationException}
     * @return 错误响应
     */
    @ExceptionHandler(HandlerMethodValidationException.class)
    public Response<?> handlerMethodValidationException(HandlerMethodValidationException e) {
        List<ParameterValidationResult> results = e.getValueResults();
        StringBuilder sb = new StringBuilder();
        StringJoiner errorParams = new StringJoiner(SymbolConstant.COMMA);
        results.forEach(result -> {
            List<MessageSourceResolvable> resolvableErrors = result.getResolvableErrors();
            resolvableErrors.forEach(resolvableError -> {
                Object[] arguments = resolvableError.getArguments();
                if (arguments != null && arguments.length == 2) {
                    DefaultMessageSourceResolvable argument = (DefaultMessageSourceResolvable) arguments[0];
                    String paramName = argument.getDefaultMessage();
                    errorParams.add(paramName);
                    sb.append("paramName [").append(paramName)
                            .append("]--[").append(result.getArgument()).append("] ")
                            .append("is not correct. It should be [");
                }
                String defaultMessage = resolvableError.getDefaultMessage();
                sb.append(defaultMessage).append("].");
            });
        });

        String msgCode = MsgCodeConst.PARAMETER_VALIDATE_FAILED;
        String msgDesc = i18n.getMessage(
                msgCode,
                new Object[]{errorParams.toString(), e.getReason()},
                MsgCodeConst.FAILED,
                getLocale());
        log.error("Find a HandlerMethodValidationException. MsgCode: {}, msgDesc: {}, details: {}",
                msgCode, msgDesc, sb, e);
        return buildResponse(msgCode, msgDesc);
    }

    /**
     * 拦截所有 {@link MissingServletRequestParameterException} 的报错
     *
     * @param e SecurityException
     * @return 错误响应
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Response<?> missingServletRequestParameterException(MissingServletRequestParameterException e) {
        String msgCode = MsgCodeConst.PARAMETER_VALIDATE_FAILED;
        String msgDesc = i18n.getMessage(msgCode, new Object[]{e.getParameterName()}, getLocale());
        log.error("Find a MissingServletRequestParameterException. MsgCode: {}, msgDesc: {}.", msgCode, msgDesc, e);
        return buildResponse(msgCode, msgDesc);
    }

    /**
     * 拦截所有 {@link SecurityException} 的报错
     *
     * @param e SecurityException
     * @return 错误响应
     */
    @ExceptionHandler(SecurityException.class)
    public Response<?> securityException(SecurityException e) {
        String msgCode = e.getMessage();
        String msgDesc = i18n.getMessage(msgCode, new Object[]{}, getLocale());
        log.error("Find a SecurityException. MsgCode: {}, msgDesc: {}.", msgCode, msgDesc, e);
        return buildResponse(msgCode, msgDesc);
    }

    /**
     * 拦截所有 {@link ParamException} 的报错
     *
     * @param e ParamException
     * @return 错误响应
     */
    @ExceptionHandler(ParamException.class)
    public Response<?> paramException(ParamException e) {
        String msgCode = MsgCodeConst.PARAMETER_ERROR;
        String errMsg = e.getMessage();
        String[] paramNames = e.getParamNames();

        String msgDesc = i18n.getMessage(msgCode, paramNames, getLocale());
        log.error("Find a ParamException. MsgCode: {}, msgDesc: {}.", msgCode, errMsg, e);
        return buildResponse(msgCode, msgDesc);
    }

    /**
     * 拦截所有 {@link ServiceCallException} 的报错
     *
     * @param e ServiceCallException
     * @return 错误响应
     */
    @ExceptionHandler(ServiceCallException.class)
    public Response<?> serviceCallException(ServiceCallException e) {
        String oriMsgCode = e.getOriMsgCode();
        if (ObjectUtil.isBlank(oriMsgCode)) {
            String msgDesc = i18n.getMessage(MsgCodeConst.SERVICE_CALL_ERROR, new Object[]{}, getLocale());
            log.error("Find a ServiceCallException. MsgCode: {}, msgDesc: {}.", oriMsgCode, msgDesc, e);
            return buildResponse(MsgCodeConst.SERVICE_CALL_ERROR, msgDesc);

        }
        String oriMsgDesc = e.getOriMsgDesc();
        log.error("Find a ServiceCallException. MsgCode: {}, msgDesc: {}.", oriMsgCode, oriMsgDesc, e);
        return buildResponse(oriMsgCode, oriMsgDesc);
    }

    /**
     * 拦截所有 {@link ActionException} 的报错
     *
     * @param e ActionException
     * @return 错误响应
     */
    @ExceptionHandler(ActionException.class)
    public Response<?> actionException(ActionException e) {
        String msgCode = e.getMessage();
        Object[] args = e.getArgs();

        String msgDesc = i18n.getMessage(msgCode, args, getLocale());
        log.error("Find a ActionException. MsgCode: {}, msgDesc: {}.", msgCode, msgDesc, e);
        return buildResponse(msgCode, msgDesc);
    }

    /**
     * 拦截所有 HalException 的报错
     *
     * @param e {@link HalException}
     * @return 错误响应
     */
    @ExceptionHandler(HalException.class)
    public Response<?> halException(HalException e) {
        String msgCode = e.getMessage();
        Object[] args = e.getArgs();
        String msgDesc = i18n.getMessage(msgCode, args, getLocale());
        log.error("Find a HalException. MsgCode: {}, msgDesc: {}.", msgCode, msgDesc, e);
        return buildResponse(msgCode, msgDesc);
    }

    /**
     * 国际化
     *
     * @param i18n 国际化
     */
    public ComponentExceptionAspect(MessageSource i18n) {
        super(i18n);
    }
}
